/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse;

import java.sql.Timestamp;

/*******************************************************************************
 * Net/FSERecord is a generic class which is extended by all Net/FSE event
 * types. It contains a variety of common methods, some abstract, that must be
 * implemented or overridden to reflect the differences in events supported by
 * Net/FSE.
 * 
 * @author bduphoff
 * 
 */
public abstract class NetFSERecord {

    /***************************************************************************
     * This switch tells Net/FSE that the record content can contain binary
     * information and needs to be converted into hex for storage in the dynamic
     * database.
     */
    public static boolean IS_BINARY = false;

    /***************************************************************************
     * The name of the type. Cannot contain spaces or special characters.
     * Example: Snort_IDS
     */
    protected String typeName = null;

    /***************************************************************************
     * The unique ID of the type associated with this record. Once an ID is used
     * by one type it cannot be used again.
     */
    protected int typeID = -1;

    /***************************************************************************
     * The sensor associated with this record.
     */
    private int sensorID = -1;

    /***************************************************************************
     * The line (or lines) of data sent to Net/FSE initially. This value will be
     * saved in Net/FSE so that records can be parsed at various stages.
     */
    private String initialString = "";

    /***************************************************************************
     * This field represents the date and time of the event/log. All events must
     * have a timestamp. In the case of records with durations, this field
     * should be used as the start timestamp.
     */
    private Timestamp timestamp;

    /***************************************************************************
     * This field represents the source IP address of the event/log as a long.
     * All events must have an IP address. A source address is nearly universal
     * so it is included in all events. This field and the srcAddr field should
     * always be set using the setSrcIP method.
     */
    private long srcIP = -1;

    /***************************************************************************
     * This method is called when indexing event streams using the static b+
     * tree index implementation. It returns field values encoded as hex strings
     * which are used to the index the record.
     * 
     * @param field
     *            A field identifier from the nfse.NetFSE.FIELD_ types.
     * @return A hex-encoded value for the specified field. Returns an empty
     *         string ("") if the field specified is not supported by Net/FSE.
     *         Currently the following fields are supported: startts, srcip,
     *         dstip, dstport.
     */
    public String get(int field) {
        String temp = "";
        int len = 0;
        switch (field) {
        case (NetFSE.FIELD_START_TS):
            temp = Long.toHexString(getStartTS().getTime() / 1000);
            len = 8;
            break;
        case (NetFSE.FIELD_DST_IP):
            temp = Long.toHexString(getDstIP());
            len = 8;
            break;
        case (NetFSE.FIELD_SRC_IP):
            temp = Long.toHexString(getSrcIP());
            len = 8;
            break;
        case (NetFSE.FIELD_DST_PORT):
            temp = Integer.toHexString(getDstPort());
            len = 4;
            break;
        default:
            temp = null;
            break;
        }
        if (temp != null) {
            while (temp.length() < len)
                temp = "0" + temp;
        }
        return temp;
    }

   

    /***************************************************************************
     * Sometimes it is appropriate to have a human-readable type name, as the
     * real type name cannot contain spaces or special characters. This method
     * should be overridden when the type name is not suitable for display. If
     * this method is not overridden then the system type name is used.
     * 
     * @return The name of the type as it will be displayed for end users in the
     *         web interface.
     */
    public String getDisplayTypeName() {
        return getTypeName();
    }

    /***************************************************************************
     * This method returns the value of getDstIP() as a dot quad (e.g. 1.2.3.4).
     * 
     * @return The destination IP address as a dot quad String.
     */
    public String getDstAddr() {
        if (getDstIP() < 0)
            return null;
        return NetFSEUtil.convertLongToIP(getDstIP());
    }

    /***************************************************************************
     * The base Net/FSE class does not have a end timestamp value so this stub
     * method returns the value of the start timestamp by calling getStartTS().
     * Event types with a duration must override this method.
     * 
     * @return -1 (stub method)
     */
    public Timestamp getEndTS() {
        return getStartTS();
    }

      

    /***************************************************************************
     * This method defines which fields are to be indexed by Net/FSE for the
     * event type. This stub method will cause Net/FSE to index events on
     * startts and srcip. It should be overridden in cases where there are
     * additional fields to index.
     * 
     * @return An integer array of NetFSE.FIELD_ values.
     */
    public abstract int[] getIndexedFields();

    /***************************************************************************
     * This method returns the value of getSrcIP() as a dot quad (e.g. 1.2.3.4).
     * 
     * @return The source IP address as a dot quad String.
     */
    public String getSrcAddr() {
        if (getSrcIP() < 0)
            return null;
        return NetFSEUtil.convertLongToIP(getSrcIP());
    }
    
    public abstract int getDstPort();
    
    public abstract long getDstIP();

    /***************************************************************************
     * The NetFSERecord base type assumes that each event type has a source IP
     * address. This is typically the case and this method can be used in
     * conjunction with setSrcIP() without modification for most event types.
     * However, these methods should be overridden in the rare case that an
     * event does not have a source IP address.
     * 
     * @return The source IP address as a long.
     */
    public long getSrcIP() {
        return srcIP;
    }

    /***************************************************************************
     * The NetFSERecord base type assumes that each event type has a start
     * timestamp. This is typically the case and this method can be used in
     * conjunction with setStartTS() without modification for most event types.
     * Net/FSE does not currently support even types that do not have some form
     * of start timestamp.
     * 
     * @return The start timestamp as a java.sql.Timestamp.
     */
    public Timestamp getStartTS() {
        return getTimestamp();
    }

    /***************************************************************************
     * This method will index all fields returned by getIndexedFields() and is
     * called when creating new dynamic tables in nfse.DataSocket.
     * 
     * @param table
     *            The name of the table to create the index statements for.
     * @return A String array of CREATE INDEX statements for the fields being
     *         indexed by Net/FSE.
     */
    public String[] getTempIndexSQL(String table) {
        int[] fields = this.getIndexedFields();
        String[] temp = new String[fields.length];
        for (int i = 0; i < temp.length; i++) {
            temp[i] = "create index " + table + "_" + NetFSE.FIELD_NAMES[fields[i]] + "_idx on " + table + " ("
                    + NetFSE.FIELD_NAMES[fields[i]] + ")";
        }
        return temp;
    }

    /***************************************************************************
     * This method creates the insert SQL statement for the event instance based
     * on the fields returned by getIndexedFields(). This method is called in
     * nfse.DataSocket.insertDynamic() when an event is to be inserted in the
     * dynamic database.
     * 
     * This method can be overridden for optimization in high-volume event types
     * but is sufficient as is in most cases. The primary overhead of this
     * method is repeated calls to getIndexedFields() where a single call is
     * sufficient.
     * 
     * @param table
     *            The table name where this event is going to be inserted.
     * @return An SQL INSERT statement for this event instance. The INSERT INTO
     *         TABLE portion is not added here to save memory but is added when
     *         nfse.DataSocket.purgeRecords() is called.
     */
    public String getTempInsertSQL() {
        StringBuilder temp = new StringBuilder("(");
        int[] fields = this.getIndexedFields();
        for (int i = 0; i < fields.length; i++) {
            switch (fields[i]) {
            case (NetFSE.FIELD_START_TS):
                temp.append("'" + getStartTS() + "'");
                break;
            case (NetFSE.FIELD_SRC_IP):
                if (getSrcIP() != -1)
                    temp.append("'" + getSrcAddr() + "'");
                else
                    temp.append("null");
                break;
            case (NetFSE.FIELD_DST_IP):
                if (getDstIP() != -1)
                    temp.append("'" + getDstAddr() + "'");
                else
                    temp.append("null");
                break;
            case (NetFSE.FIELD_DST_PORT):
                temp.append(getDstPort());
                break;
            default:
                break;
            }
            temp.append(",");
        }

        if (hasBinary()) {
            temp.append("'','");
            temp.append(NetFSEUtil.convertToHex(toRecord()));
            temp.append("'");
        } else {
            temp.append("'");
            temp.append(toRecord().replaceAll("'", " "));
            temp.append("',''");
        }
        temp.append(")");
        // System.out.println(temp.toString());
        return temp.toString();
    }

    /**
     * Creates a tab-delimited representation of this record, allowing it to be
     * rapidly inserted into the dynamic database processing engine.
     * 
     * @return The tab-delimited representation of this record for use in
     *         PostgreSQL's COPY command
     */
    public String getTempCopySQL() {
        StringBuilder temp = new StringBuilder("");
        int[] fields = this.getIndexedFields();
        for (int i = 0; i < fields.length; i++) {
            switch (fields[i]) {
            case (NetFSE.FIELD_START_TS):
                temp.append("'" + getStartTS() + "'");
                break;
            case (NetFSE.FIELD_SRC_IP):
                if (getSrcIP() != -1)
                    temp.append("" + getSrcAddr() + "");
                else
                    temp.append("\\N");
                break;
            case (NetFSE.FIELD_DST_IP):
                if (getDstIP() != -1)
                    temp.append("" + getDstAddr() + "");
                else
                    temp.append("\\N");
                break;
            case (NetFSE.FIELD_DST_PORT):
                temp.append(getDstPort());
                break;
            default:
                break;
            }
            temp.append("\t");
        }

        if (hasBinary()) {
            temp.append("\t");
            temp.append(NetFSEUtil.convertToHex(toRecord()));
            // temp.append("'");
        } else {
            // temp.append("'");
            temp.append(toRecord().replaceAll("'", " ").replaceAll("\t", "   "));
            temp.append("\t");
        }
        // temp.append(")");
        // System.out.println(temp.toString());
        return temp.toString();
    }

    /***************************************************************************
     * This method will generate a CREATE TABLE SQL statement for this event
     * type. All fields returned by getIndexedFields() are added to the table.
     * It is called when creating new dynamic tables in nfse.DataSocket.
     * 
     * @param table
     *            The name of the table to create.
     * @return A CREATE TABLE statement for the fields being indexed by Net/FSE.
     */
    public String getTempTableSQL(String table) {
        StringBuilder temp = new StringBuilder("create table ");
        temp.append(table);
        temp.append("(");
        int[] fields = this.getIndexedFields();
        for (int i = 0; i < fields.length; i++) {
            switch (fields[i]) {
            case (NetFSE.FIELD_START_TS):
                temp.append("startts timestamp");
                break;
            case (NetFSE.FIELD_SRC_IP):
                temp.append("srcip inet");
                break;
            case (NetFSE.FIELD_DST_IP):
                temp.append("dstip inet");
                break;
            case (NetFSE.FIELD_DST_PORT):
                temp.append("dstport int");
                break;
            default:
                break;
            }
            temp.append(",");
        }

        temp.append("record text");
        temp.append(",");
        temp.append("record_hex text");
        temp.append(") ");
        // System.out.println(temp.toString());
        return temp.toString();
    }

    /***************************************************************************
     * Gets the unique type ID for this event type. This value is specified in
     * the Net/FSE configuration process (see nfse.NetFSEServer.conifgure()).
     * 
     * @return The unique event type ID.
     */
    public int getTypeID() {
        return typeID;
    }

    /***************************************************************************
     * Gets the unique type name for this event type. This value is specified in
     * the Net/FSE configuration process (see nfse.NetFSEServer.conifgure()).
     * 
     * @return The unique event type name.
     */
    public String getTypeName() {
        return typeName;
    }

    /***************************************************************************
     * This method can be overridden depending on if the event type contains
     * binary data. It should return true is this is the case, instead of the
     * default of false returned by this stub method.
     * 
     * @return The IS_BINARY field.
     */
    public boolean hasBinary() {
        return IS_BINARY;
    }

    /***************************************************************************
     * This method performs no operation by default. It should be overridden by
     * event type implementations with destination IP addresses and should set
     * the values of dstIP and dstAddr.
     * 
     */
    // public void setDstIP(long dstIP) {
    // no op
    // }
    /***************************************************************************
     * This method sets the srcIP and srcAddr fields based on the IP passed in
     * (as a long). Not typically overridden except in the rare case that there
     * is no source IP.
     * 
     * @param srcIP
     *            The source IP address as a 32-bit long.
     */
    public void setSrcIP(long srcIP) {
        this.srcIP = srcIP;
    }

    /***************************************************************************
     * Sets the timestamp field. All Net/FSE event types must have a timestamp
     * of some sort.
     * 
     * @param startTS
     */
    public void setStartTS(Timestamp startTS) {
        this.setTimestamp(startTS);
    }

    /***************************************************************************
     * Returns the initial, unparsed String value for this event type. Should
     * not be overridden.
     * 
     * @return The unparsed String that was sent initially to Net/FSE.
     */
    public String toRecord() {
        return getInitialString();
    }
    
    /***************************************************************************
     * Net/FSE handles syslog events differently from non-syslog events.
     * Typically, syslog events have timestamp or IP information in the syslog
     * header. If this method returns true, the syslog header will be maintained
     * by nfse.SyslogThread. If it returns false the header will be discarded.
     * This method should be overridden to return true for any event type that
     * requires the syslog header for obtaining field information needed by
     * Net/FSE (i.e. timestamps and IPs).
     * 
     * @return This stub method returns false, causing the syslog header to be
     *         discarded.
     */
    public boolean isSyslog() {
        return false;
    }

    public boolean isAlert() {
        return false;
    }

    public String getAlertMessage() {
        return "";
    }

    public String toXML() {
        return "<" + getTypeName() + " sensorid=\"" + getSensorID() + "\">toXML() method not implemented for type "
                + getTypeID() + "</" + getTypeName() + ">";
    }

    /**
     * @param typeName
     *            the typeName to set
     */
    public void setTypeName(String typeName) {
        this.typeName = typeName;
    }

    /**
     * @param typeID
     *            the typeID to set
     */
    public void setTypeID(int typeID) {
        this.typeID = typeID;
    }

    /**
     * @param sensorID
     *            the sensorID to set
     */
    public void setSensorID(int sensorID) {
        this.sensorID = sensorID;
    }

    /**
     * @return the sensorID
     */
    public int getSensorID() {
        return sensorID;
    }

    /**
     * @param initialString
     *            the initialString to set
     */
    public void setInitialString(String initialString) {
        this.initialString = initialString;
    }

    /**
     * @return the initialString
     */
    public String getInitialString() {
        return initialString;
    }

    /**
     * @param timestamp
     *            the timestamp to set
     */
    public void setTimestamp(Timestamp timestamp) {
        this.timestamp = timestamp;
    }

    /**
     * @return the timestamp
     */
    public Timestamp getTimestamp() {
        return timestamp;
    }

    /**
     * @param srcAddr
     *            the srcAddr to set
     */
    public void setSrcAddr(String srcAddr) {
        this.srcIP = NetFSEUtil.convertIPtoLong(srcAddr);
    }

    
}
